View4D_BRIK.m 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813
  1. function varargout = View4D_BRIK(varargin)
  2. % Viewer for 4D .BRIK file.
  3. % - Allows for montages across both slice dimensions and time.
  4. % - Also displays timeseries for selected voxel.
  5. %
  6. % Usage:
  7. % View4D_BRIK(anat_img, brik_img, cellarray_of_other_funcdata, custom_legendnames)
  8. %
  9. % Required Inputs:
  10. % anat_img: Is your anatomical+tlrc.BRIK that serves as underlay.
  11. % brik_img: Is your the functional+tlrc.BRIK file that serves as overlay.
  12. %
  13. % Optional Inputs (Can leave empty if not desired):
  14. % cellarray_of_other_funcdata:
  15. % - In the viewer, timeseries data of the selected voxel for the primary functional image
  16. % is plotted in a solid line. If you wish to overlay timeseries data from other functional
  17. % data onto the plot, you can specify the other datasets in the third argument.
  18. % - If left empty, only the primary functional image will be plotted.
  19. %
  20. % custom_legendnames:
  21. % - Allows you to specify your own legend for the voxel timeseries plot.
  22. % - If left empty, dataset names will be used.
  23. %
  24. %
  25. % Examples:
  26. % View4D_BRIK('anat+tlrc.BRIK', 'functional+tlrc.BRIK', [], [])
  27. %
  28. % View4D_BRIK('anat+tlrc.BRIK', 'Cond1+tlrc.BRIK', ...
  29. % {'Cond2+tlrc.BRIK, ControlCond+tlrc.BRIK'}, {'Cond1', 'Cond2', Control'})
  30. %
  31. % Written by: Natasa Kovacevic
  32. % Modified in areas marked with ** (Nov, 2013: M. Cheung)
  33. % Last modified: Jan. 15, 2014
  34. % Copyright (C) 2013-2014, Natasa Kovacevic
  35. %
  36. % This file is a part of the MEG & PLS Pipeline (MEGPLS). For more
  37. % details, see the documentation included with the software package.
  38. %
  39. % MEGPLS is free software: you can redistribute it and/or modify it under
  40. % the terms of the GNU General Public License version 2 as published by
  41. % the Free Software Foundation. This program is distributed in the hope
  42. % that it will be useful, but WITHOUT ANY WARRANTY; without even the
  43. % implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  44. % See the GNU General Public License for more details.
  45. %
  46. % You should have received a copy of the GNU General Public License along
  47. % with this program. If not, you can download the license here:
  48. % <http://www.gnu.org/licenses/old-licenses/gpl-2.0>.
  49. % NK: note use set/get for fields that come prediscribed with objects (e.g. sliders)
  50. % and for fields that I added to handles. Use setappdata/getappdata for gui
  51. % Begin initialization code - DO NOT EDIT
  52. gui_Singleton = 0;
  53. gui_State = struct('gui_Name', mfilename, ...
  54. 'gui_Singleton', gui_Singleton, ...
  55. 'gui_OpeningFcn', @View4D_BRIK_OpeningFcn, ...
  56. 'gui_OutputFcn', @View4D_BRIK_OutputFcn, ...
  57. 'gui_LayoutFcn', [] , ...
  58. 'gui_Callback', []);
  59. if nargin && ischar(varargin{1})
  60. gui_State.gui_Callback = str2func(varargin{1});
  61. end
  62. if nargout
  63. [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
  64. else
  65. gui_mainfcn(gui_State, varargin{:});
  66. end
  67. % End initialization code - DO NOT EDIT
  68. %--- Executes just before View4D_BRIK is made visible. ---%
  69. %---------------------------------------------------------%
  70. function View4D_BRIK_OpeningFcn(hObject, eventdata, handles, varargin)
  71. % This function has no output args, see OutputFcn.
  72. % hObject handle to figure
  73. % eventdata reserved - to be defined in a future version of MATLAB
  74. % handles structure with handles and user data (see GUIDATA)
  75. % varargin command line arguments to View4D_BRIK (see VARARGIN)
  76. % Choose default command line output for View4D_BRIK
  77. handles.output = hObject;
  78. % Update handles structure
  79. guidata(hObject, handles);
  80. % Check inputs:
  81. if numel(varargin) < 4 | numel(varargin)>4
  82. disp('You must supply: anatomical underlay and functional 4D .BRIK file');
  83. disp('All files should be in AFNI format');
  84. disp('Usage: View4D_BSR(anat_img, brik_img, cellarray_of_otherdata, customlegendnames)');
  85. disp('Example: View4D_BSR(''anat+tlrc.BRIK'', ''functional+tlrc.BRIK'', [], [])');
  86. error('wrong number of inputs ');
  87. end
  88. % load anatomical BRIK file
  89. Opt.format = 'vector';
  90. [err,anat,Info,ErrMessage]=BrikLoad(varargin{1},Opt);
  91. handles.anat = anat;
  92. % load functional 4D .BRIK image:
  93. Opt.format = 'vector';
  94. [err,img,Info,ErrMessage]=BrikLoad(varargin{2},Opt);
  95. BSR.img = img;
  96. handles.dims = size(BSR.img); % 4-dimensional (x, y, z, time)
  97. if numel(size(BSR.img))==3, handles.dims(4) = 1; end
  98. handles.BSR = BSR;
  99. handles.Info = Info;
  100. handles.MainFuncFile = varargin{2};
  101. guidata(hObject, handles);
  102. %** Add ability to overlay multiple datasets in voxel timeseries axes.
  103. % load other .BRIK data to include in voxel timeseries:
  104. if ~isempty(varargin{3})
  105. for l=1:length(varargin{3})
  106. Opt.format = 'vector';
  107. [err,OtherImgData{l},Info,ErrMessage]=BrikLoad(varargin{3}{l},Opt);
  108. % Check dims of other image data.
  109. if size(OtherImgData{l}) ~= size(BSR.img)
  110. error('Optional datasets must be same dimension as the main functional image.');
  111. end
  112. end
  113. handles.OtherImgData = OtherImgData;
  114. handles.OtherImgName = varargin{3};
  115. else
  116. handles.OtherImgData = [];
  117. handles.OtherImgName = [];
  118. end
  119. %** Add ability to specify own legend in voxel-timeseries plot:
  120. if ~isempty(varargin{4})
  121. handles.CustomLegend = varargin{4};
  122. if length(handles.CustomLegend) ~= length(handles.OtherImgData) + 1
  123. disp('Error: Custom legend does not contain correct number of inputs.');
  124. disp(' - Legend names should include both the main functional image and optional datasets.');
  125. error('Custom Legend has wrong number of inputs.');
  126. end
  127. else
  128. handles.CustomLegend = [];
  129. end
  130. % Sets title to PLS LV info:
  131. %tmp=strfind(Info.HISTORY_NOTE, '***');
  132. % if ~isempty(tmp)
  133. % tmp=Info.HISTORY_NOTE(tmp:end);
  134. % loc=strfind(tmp,'\n');
  135. % finaltmp=tmp(1:(loc(1)-1));
  136. % set(handles.Title,'String',finaltmp);
  137. % else
  138. %** Changed to just display full filepath as title
  139. set(handles.Title,'String',varargin{2});
  140. %end
  141. % set view and sliders
  142. x = ceil(handles.dims(1)/2);
  143. y = ceil(handles.dims(2)/2);
  144. z = ceil(handles.dims(3)/2);
  145. set(handles.AxialSlider,'Min',1,'Max',handles.dims(3),'SliderStep',[1/(handles.dims(3)-1) 1/(handles.dims(3)-1)],'Value',z);
  146. set(handles.SagitalSlider,'Min',1,'Max',handles.dims(1),'SliderStep',[1/(handles.dims(1)-1) 1/(handles.dims(1)-1)],'Value',x);
  147. set(handles.CoronalSlider,'Min',1,'Max',handles.dims(2),'SliderStep',[1/(handles.dims(2)-1) 1/(handles.dims(2)-1)],'Value',y);
  148. setappdata(handles.Axialview,'slicenum',z);
  149. setappdata(handles.Sagitalview,'slicenum',x);
  150. setappdata(handles.Coronalview,'slicenum',y);
  151. if numel(size(BSR.img))==3
  152. handles.dims(4) = 1;
  153. set(handles.TimeptSlider,'Min',1,'Max',1.001,'SliderStep',[1 1],'Value',1);
  154. else
  155. set(handles.TimeptSlider,'Min',1,'Max',handles.dims(4),'SliderStep',[1/(handles.dims(4)-1) 1/(handles.dims(4)-1)],'Value',1);
  156. end
  157. % set up montage selection listbox and monatge viewing parameters
  158. set(handles.SelectMontage,'String',{'None','Axial','Sagittal','Coronal'});
  159. set(handles.MontageViewingTable,'data',[1 1 handles.dims(1); 1 1 handles.dims(2); 1 1 handles.dims(3); 1 1 3;]);
  160. % start with first timept
  161. set(handles.Timept,'String','1');
  162. % set up bsr, tbsr, blend
  163. UpdateBlend(handles);
  164. % start using gui
  165. UpdateSlices(x,y,z,handles);
  166. UpdateTimept(1,handles);
  167. guidata(hObject, handles); % Update handles structure
  168. function varargout = View4D_BRIK_OutputFcn(hObject, eventdata, handles)
  169. varargout{1} = handles.output;
  170. function UpdateBlend(handles)
  171. % set up bsr, tbsr, blend
  172. % this function is normally called only in the begining and when user
  173. % changes bsr thresholds -> because thhreshold is the only thing that
  174. % alters the blend and the colormap
  175. % clamp bsr image for better viewing
  176. %** get clamp range from textbox:
  177. %bsr_clamp = 5;
  178. bsr_clamp = str2num(get(handles.TextboxClampRange, 'String'));
  179. bsr = handles.BSR.img;
  180. bsr(find(bsr>=bsr_clamp)) = bsr_clamp; bsr(find(bsr<=-bsr_clamp)) = -bsr_clamp;
  181. % get bsr threshold
  182. %** Added check to prevent threshold from exceeding max/min of data or clamp
  183. posthresh = str2num(get(handles.BSR_posthresh,'String'));
  184. negthresh = str2num(get(handles.BSR_negthresh,'String'));
  185. maxvalue = getappdata(handles.BSR_posthresh,'d');
  186. minvalue = getappdata(handles.BSR_posthresh,'c');
  187. if posthresh > maxvalue | posthresh <= minvalue
  188. posthresh = maxvalue;
  189. set(handles.BSR_posthresh,'String', posthresh);
  190. end
  191. if negthresh < minvalue | negthresh >= maxvalue
  192. negthresh = minvalue;
  193. set(handles.BSR_negthresh,'String', negthresh);
  194. end
  195. % zero out bsr voxels below threshold
  196. tbsr = bsr;
  197. tbsr(find(tbsr<posthresh & tbsr>negthresh)) = 0;
  198. orig_tbsr = tbsr;
  199. % now map tbsr for colormap
  200. d = max(max(max(max(bsr)))); c = min(min(min(min(bsr))));
  201. if c==d, % that would happen only if tbsr is zero everywhere
  202. tbsr = ones(size(tbsr));
  203. else
  204. tbsr = round(255*(tbsr-c)/(d-c)+1);
  205. end
  206. [cmap, ignore_pts] = set_colormap(d,c,posthresh, negthresh);
  207. colormap(cmap);
  208. % map grayscale image into midwindow in the colormap
  209. img = handles.anat;
  210. a = min(min(min(img)));
  211. b = max(max(max(img)));
  212. img = round(ignore_pts(1) + (ignore_pts(end)-ignore_pts(1))*(img-a)/(b-a));
  213. img = min(max(img,ignore_pts(1)),ignore_pts(end));
  214. img = repmat(img,[1 1 1 handles.dims(4)]);
  215. blend = zeros([size(tbsr) 3]);
  216. for dim=1:3
  217. utmp = reshape(cmap(img,dim),size(tbsr));
  218. otmp = reshape(cmap(tbsr,dim),size(tbsr));
  219. blend(:,:,:,:,dim)= (orig_tbsr==0).* utmp + (orig_tbsr~=0).* otmp;
  220. end
  221. % attach blend and colormap related infor to be used in other gui
  222. % components
  223. setappdata(handles.BSR_posthresh,'blend',blend);
  224. setappdata(handles.BSR_posthresh,'cmap',cmap);
  225. setappdata(handles.BSR_posthresh,'ignore_pts', ignore_pts);
  226. setappdata(handles.BSR_posthresh,'c',c);
  227. setappdata(handles.BSR_posthresh,'d',d);
  228. function UpdateSlices(x,y,z,handles)
  229. % get current bsr thresholds
  230. posthresh = str2num(get(handles.BSR_posthresh,'String'));
  231. negthresh = str2num(get(handles.BSR_negthresh,'String'));
  232. bsr = handles.BSR.img;
  233. % get current time point and make sure that it makes sense
  234. tpt = round(get(handles.TimeptSlider,'value'));
  235. % get blend and colormap
  236. blend_allt= getappdata(handles.BSR_posthresh,'blend');
  237. blend = squeeze(blend_allt(:,:,:,tpt,:));
  238. cmap = getappdata(handles.BSR_posthresh,'cmap');
  239. colormap(cmap);
  240. % AFNI IJK coordinates:
  241. set(handles.Coord,'String',[num2str(x) ' ' num2str(y) ' ' num2str(z)]);
  242. % AFNI indexing convention for AFNI viewer & functions starts at 0.
  243. % Note: AFNI reads orientation in voxel-storage order, so LPI in AFNI is RAS (in spatial direction)
  244. % Recall: RAS is orientation of coordinate systems of TLRC and MNI templates.
  245. [err, XYZdic] = AFNI_Index2XYZcontinuous([x-1, y-1, z-1], handles.Info, 'LPI');
  246. set(handles.MNI_coord, 'String', [num2str(XYZdic(1)) ' ' num2str(XYZdic(2)) ' ' num2str(XYZdic(3))]);
  247. % udate slider positions
  248. set(handles.AxialSlider,'Value',z);
  249. set(handles.SagitalSlider,'Value',x);
  250. set(handles.CoronalSlider,'Value',y);
  251. setappdata(handles.Axialview,'slicenum',z);
  252. setappdata(handles.Sagitalview,'slicenum',x);
  253. setappdata(handles.Coronalview,'slicenum',y);
  254. % update slice views
  255. col = [0 0.8 0];
  256. axes(handles.Axialview); % make this current axes
  257. tmp=permute(squeeze(blend(:,:,z,:)),[2 1 3]);
  258. image(tmp,'ButtonDownFcn',{@click_axial,handles});
  259. set(handles.Axialview,'ydir','normal','xtick',[],'ytick',[]);
  260. line([x x],[1 handles.dims(2)],'color',[0 1 0],'visible','on');
  261. line([1 handles.dims(1)],[y y],'color',[0 1 0],'visible','on');
  262. text(2,2,['z=' num2str(z)],'fontsize',12,'fontweight','bold','color',[0 1 0.3]);
  263. axes(handles.Sagitalview); % make this current axes
  264. image(permute(squeeze(blend(x,:,:,:)),[2 1 3]),'ButtonDownFcn',{@click_sagital,handles});
  265. set(handles.Sagitalview,'ydir','normal','xtick',[],'ytick',[]);
  266. line([y y],[1 handles.dims(3)],'color',[0 1 0],'visible','on');
  267. line([1 handles.dims(2)],[z z],'color',[0 1 0],'visible','on');
  268. text(2,2,['x=' num2str(x)],'color','g','fontsize',12,'fontweight','bold');
  269. text(0,handles.dims(3)+2,num2str(bsr(x,y,z,tpt)),'fontsize',12,'fontweight','bold','color','c');
  270. axes(handles.Coronalview); % make this current axes
  271. image(permute(squeeze(blend(:,y,:,:)),[2 1 3]),'ButtonDownFcn',{@click_coronal,handles});
  272. set(handles.Coronalview,'ydir','normal','xtick',[],'ytick',[]);
  273. line([x x],[1 handles.dims(3)],'color',[0 1 0],'visible','on');
  274. line([1 handles.dims(1)],[z z],'color',[0 1 0],'visible','on');
  275. text(2,2,['y=' num2str(y)],'color','g','fontsize',12,'fontweight','bold');
  276. text(0,handles.dims(3)+2,num2str(bsr(x,y,z,tpt)),'fontsize',12,'fontweight','bold','color','m');
  277. % update BSR colorbar
  278. axes(handles.colorbar_view);
  279. imagesc([1:256]'); colormap(cmap);
  280. ignore_pts = getappdata(handles.BSR_posthresh,'ignore_pts');
  281. %**Commented out: Modified below to be more robust accomodating different datasets
  282. %set(gca, 'ydir', 'normal', 'ytick',[1 ignore_pts(1) ignore_pts(end) 256], 'YTickLabel', {num2str(getappdata(handles.BSR_posthresh,'c')), num2str(negthresh), num2str(posthresh), num2str(getappdata(handles.BSR_posthresh,'d'))}, 'XTickLabel', []);
  283. if ignore_pts(1) <= 1
  284. set(gca, 'ydir', 'normal', 'ytick',[1 ignore_pts(end) 256], 'YTickLabel', {num2str(getappdata(handles.BSR_posthresh,'c')), num2str(posthresh), num2str(getappdata(handles.BSR_posthresh,'d'))}, 'XTickLabel', []);
  285. elseif ignore_pts(end) >= 256
  286. set(gca, 'ydir', 'normal', 'ytick',[1 ignore_pts(1) 256], 'YTickLabel', {num2str(getappdata(handles.BSR_posthresh,'c')), num2str(negthresh), num2str(getappdata(handles.BSR_posthresh,'d'))}, 'XTickLabel', []);
  287. elseif ignore_pts(1) <= 1 && ignore_pts(end) >= 256
  288. set(gca, 'ydir', 'normal', 'ytick',[1 256], 'YTickLabel', {num2str(getappdata(handles.BSR_posthresh,'c')), num2str(getappdata(handles.BSR_posthresh,'d'))}, 'XTickLabel', []);
  289. else
  290. set(gca, 'ydir', 'normal', 'ytick',[1 ignore_pts(1) ignore_pts(end) 256], 'YTickLabel', {num2str(getappdata(handles.BSR_posthresh,'c')), num2str(negthresh), num2str(posthresh), num2str(getappdata(handles.BSR_posthresh,'d'))}, 'XTickLabel', []);
  291. end
  292. % update BSR time series plot
  293. axes(handles.BSR_timeseries);
  294. Tseries = squeeze(handles.BSR.img(x,y,z,:));
  295. if numel(Tseries) > 1
  296. at = min(Tseries); bt = max(Tseries);
  297. num_tpts = numel(Tseries);
  298. tpts = [1:num_tpts];
  299. plot(tpts,Tseries, 'LineStyle', '-', 'LineWidth', 2);
  300. hold on;
  301. if bt > at
  302. e = (bt-at)/20;
  303. set(gca,'ylim',[at-e bt+e],'xlim',[0 numel(Tseries)+1]);
  304. else
  305. set(gca,'ylim',[-1 1],'xlim',[0 numel(Tseries)+1]);
  306. end
  307. hold all;
  308. end
  309. %** Add ability to overlay multiple datasets in voxel timeseries axes.
  310. % overlay other specified datasets onto timeseries plot
  311. if ~isempty(handles.OtherImgData)
  312. num_OtherData = length(handles.OtherImgData);
  313. tmparray = zeros(num_OtherData, length(Tseries));
  314. for l=1:num_OtherData
  315. tmparray(l,:) = squeeze(handles.OtherImgData{l}(x,y,z,:));
  316. end
  317. tmparray = tmparray';
  318. plot(tmparray, 'LineStyle', '-', 'LineWidth', 1);
  319. hold on;
  320. %** add ability for custom legendname
  321. if ~isempty(handles.CustomLegend)
  322. clickableLegend(handles.CustomLegend, 'interpreter', 'none');
  323. else
  324. [~, FuncFile, ~] = fileparts(handles.MainFuncFile);
  325. LegendData{1} = FuncFile;
  326. for f = 1:num_OtherData
  327. [~, OtherFile, ~] = fileparts(handles.OtherImgName{f});
  328. LegendData{f+1} = OtherFile;
  329. end
  330. clickableLegend(LegendData, 'interpreter', 'none');
  331. end
  332. end
  333. % horizontal dotted red lines indicate bsr thresholds
  334. h=line([1 num_tpts], [posthresh posthresh]);
  335. set(h, 'LineStyle', '--', 'Color', 'r');
  336. h=line([1 num_tpts], [negthresh negthresh]);
  337. set(h, 'LineStyle', '--', 'Color', 'r');
  338. hold off;
  339. title(['Value = ',num2str(Tseries(tpt))],'color','r','fontsize',14);
  340. minval = min(min(Tseries),negthresh); maxval = max(max(Tseries),posthresh);
  341. E = (maxval - minval)/10;
  342. set(gca,'ylim',[minval-E maxval+E]);
  343. % vertical gray line indicates current tpt
  344. h = line([tpt tpt], [minval-E maxval+E]);
  345. set(h, 'LineStyle', '-', 'Color', [0.6 0.6 0.6]);
  346. set(gca,'ButtonDownFcn',{@click_BSR_ts,handles},'xlim',[0 num_tpts+1]);
  347. setappdata(handles.PrintBSRButton,'bsr_ts',Tseries);
  348. function UpdateTimept(tpt,handles)
  349. % update views
  350. x = getappdata(handles.Sagitalview,'slicenum');
  351. y = getappdata(handles.Coronalview,'slicenum');
  352. z = getappdata(handles.Axialview,'slicenum');
  353. UpdateSlices(x,y,z,handles);
  354. function AxialSlider_Callback(hObject, eventdata, handles)
  355. z = round(get(hObject, 'Value'));
  356. x = round(get(handles.SagitalSlider,'Value'));
  357. y = round(get(handles.CoronalSlider,'Value'));
  358. UpdateSlices(x,y,z,handles);
  359. function SagitalSlider_Callback(hObject, eventdata, handles)
  360. x = round(get(hObject, 'Value'));
  361. z = round(get(handles.AxialSlider,'Value'));
  362. y = round(get(handles.CoronalSlider,'Value'));
  363. UpdateSlices(x,y,z,handles);
  364. function CoronalSlider_Callback(hObject, eventdata, handles)
  365. y = round(get(hObject, 'Value'));
  366. z = round(get(handles.AxialSlider,'Value'));
  367. x = round(get(handles.SagitalSlider,'Value'));
  368. UpdateSlices(x,y,z,handles);
  369. function Timept_Callback(hObject, eventdata, handles)
  370. tpt = round(str2num(get(hObject,'String')));
  371. if tpt<1 || tpt>handles.dims(4)
  372. prompt={
  373. ['Invalid Time.'];
  374. ['Time must be # between: 1 & ',num2str(handles.dims(4))]};
  375. warndlg(prompt,'Warning:','modal');
  376. end
  377. set(handles.TimeptSlider,'Value',tpt);
  378. UpdateTimept(tpt,handles);
  379. function TimeptSlider_Callback(hObject, eventdata, handles)
  380. tpt = round(get(hObject, 'Value'));
  381. set(handles.Timept,'String',num2str(tpt));
  382. UpdateTimept(tpt,handles);
  383. function click_axial(src,eventdata,handles)%callback for buttonclick
  384. c= ginput(1);
  385. x = round(c(1,1));
  386. y = round(c(1,2));
  387. z = getappdata(handles.Axialview,'slicenum');
  388. UpdateSlices(x,y,z,handles);
  389. function click_sagital(src,eventdata,handles)
  390. %c = get(gca,'CurrentPoint');
  391. c= ginput(1);
  392. y = round(c(1,1));
  393. z = round(c(1,2));
  394. x = getappdata(handles.Sagitalview,'slicenum');
  395. UpdateSlices(x,y,z,handles);
  396. function click_coronal(src,eventdata,handles)
  397. %c = get(gca,'CurrentPoint');
  398. c= ginput(1);
  399. x = round(c(1,1));
  400. z = round(c(1,2));
  401. y = getappdata(handles.Coronalview,'slicenum');
  402. UpdateSlices(x,y,z,handles);
  403. function click_BSR_ts(src,eventdata,handles)
  404. c = ginput(1);
  405. tpt = round(c(1));
  406. tpt = min(max(1,tpt), handles.dims(4));
  407. set(handles.Timept,'String',num2str(tpt));
  408. set(handles.TimeptSlider,'value',tpt);
  409. UpdateTimept(tpt,handles);
  410. function click_GPAVG_ts(src,eventdata,handles)
  411. c = ginput(1);
  412. tpt = round(c(1));
  413. tpt = min(max(1,tpt), handles.dims(4));
  414. set(handles.Timept,'String',tpt);
  415. set(handles.TimeptSlider,'value',tpt);
  416. UpdateTimept(tpt,handles);
  417. function Coord_Callback(hObject, eventdata, handles) % user eneters voxel coordinates
  418. coord = round(str2num(get(hObject,'String')));
  419. if (coord(1)<1 || coord(1)>handles.dims(1)) || (coord(2)<1 || coord(2)>handles.dims(2)) || (coord(3)<1 || coord(2)>handles.dims(3))
  420. prompt={
  421. ['Invalid Coordinate.'];
  422. ['X-coord must be # between: 1 & ',num2str(handles.dims(1))];
  423. ['Y-coord must be # between: 1 & ',num2str(handles.dims(2))];
  424. ['Z-coord must be # between: 1 & ',num2str(handles.dims(3))]};
  425. warndlg(prompt,'Warning:','modal');
  426. end
  427. UpdateSlices(coord(1),coord(2),coord(3),handles);
  428. function MNI_coord_Callback(hObject, eventdata, handles)
  429. mni_coord = round(str2num(get(hObject,'String')));
  430. [err, coord] = AFNI_XYZcontinuous2Index(mni_coord, handles.Info, 'LPI', 3);
  431. coord = coord+1; % AFNI indexing convention starts at 0.
  432. UpdateSlices(coord(1),coord(2),coord(3),handles);
  433. function BSR_negthresh_Callback(hObject, eventdata, handles)
  434. thresh = str2num(get(handles.BSR_negthresh,'String'));
  435. if thresh>0, set(handles.BSR_negthresh,'String','0'); end
  436. UpdateBlend(handles);
  437. z = getappdata(handles.Axialview,'slicenum');
  438. x = getappdata(handles.Sagitalview,'slicenum');
  439. y = getappdata(handles.Coronalview,'slicenum');
  440. UpdateSlices(x,y,z,handles);
  441. function BSR_posthresh_Callback(hObject, eventdata, handles)
  442. thresh = str2num(get(handles.BSR_posthresh,'String'));
  443. if thresh<0, set(handles.BSR_posthresh,'String','0'); end
  444. UpdateBlend(handles);
  445. z = getappdata(handles.Axialview,'slicenum');
  446. x = getappdata(handles.Sagitalview,'slicenum');
  447. y = getappdata(handles.Coronalview,'slicenum');
  448. UpdateSlices(x,y,z,handles);
  449. %** Added textbox for clamp range
  450. function TextboxClampRange_Callback(hObject, eventdata, handles)
  451. clamp = str2num(get(handles.TextboxClampRange, 'String'));
  452. if clamp<=0, set(handles.TextboxClampRange, 'String', '10'); end
  453. UpdateBlend(handles);
  454. z = getappdata(handles.Axialview,'slicenum');
  455. x = getappdata(handles.Sagitalview,'slicenum');
  456. y = getappdata(handles.Coronalview,'slicenum');
  457. UpdateSlices(x,y,z,handles);
  458. function SelectMontage_Callback(hObject, eventdata, handles)
  459. contents = cellstr(get(hObject,'String')); % get the whole list as a cell array of strings
  460. montage_type = contents{get(hObject,'Value')}; % get current selection
  461. if strmatch(montage_type,{'Axial','Sagittal','Coronal'})
  462. TableData = get(handles.MontageViewingTable,'Data'); % get User defined parmaeters for the montage
  463. figure('NumberTitle','off','name',['Montage ' montage_type]);
  464. dims = handles.dims;
  465. % get blend and subsample according to the montage table parameters
  466. blend = getappdata(handles.BSR_posthresh,'blend');
  467. subblend = blend(TableData(1,1):TableData(1,2):TableData(1,3),TableData(2,1):TableData(2,2):TableData(2,3),TableData(3,1):TableData(3,2):TableData(3,3),TableData(4,1):TableData(4,2):TableData(4,3),:); % (x y z t rgb)
  468. % determine axes permuation order
  469. switch montage_type
  470. case 'Axial', ord = [1 2 3 4]; xlab = 'z slice';
  471. case 'Sagittal', ord = [2 3 1 4]; xlab = 'x slice';
  472. case 'Coronal', ord = [1 3 2 4]; xlab = 'y slice';
  473. end
  474. % permute subblend in awy that is good for the display
  475. subblend = permute(subblend,[ord(2) ord(4) ord(1) ord(3) 5]); % 5th dim is rgb
  476. for d=1:4
  477. span{d} = TableData(ord(d),1):TableData(ord(d),2):TableData(ord(d),3);
  478. end
  479. % determine vertical image size (dim1) and horizontal image size (dim2)
  480. dim1 = numel(span{2}) * numel(span{4}); % rows = ysize*time
  481. dim2 = numel(span{1}) * numel(span{3}); % columns = xsize*zsize
  482. % reshape subblend and show
  483. subblend = reshape(subblend,dim1,dim2,3);
  484. image(subblend,'ButtonDownFcn',{@click_Montage,handles});
  485. set(gca,'ydir','normal','xtick',([1:numel(span{3})]-0.5)*numel(span{1}),'xticklabel',cellstr(int2str(span{3}'))','ytick',([1:numel(span{4})]-0.5)*numel(span{2}),'yticklabel',cellstr(int2str(span{4}'))');
  486. ylabel('Time');
  487. xlabel(xlab);
  488. % get current gui (x y z t) coordinate
  489. z = getappdata(handles.Axialview,'slicenum');
  490. x = getappdata(handles.Sagitalview,'slicenum');
  491. y = getappdata(handles.Coronalview,'slicenum');
  492. t = round(get(handles.TimeptSlider,'value'));
  493. coord = [x y z t];
  494. % tranaslate this coordinate into this image space
  495. for d=1:4, subcoord(d) = round((coord(d)-TableData(d,1))/TableData(d,2))+1; end
  496. subcoord = subcoord(ord);
  497. % plot crosshair and
  498. horpos = numel(span{2})*(subcoord(4)-1) + subcoord(2);
  499. verpos = numel(span{1})*(subcoord(3)-1) + subcoord(1);
  500. horline = line([1 dim2],[horpos horpos],'color',[0 1 0],'visible','on');
  501. verline = line([verpos verpos],[1 dim1],'color',[0 1 0],'visible','on');
  502. % record current montage window settings
  503. setappdata(handles.MontageViewingTable,'span',span);
  504. setappdata(handles.MontageViewingTable,'ord',ord);
  505. setappdata(handles.MontageViewingTable,'subcoord',subcoord);
  506. setappdata(handles.MontageViewingTable,'fig',gcf);
  507. setappdata(handles.MontageViewingTable,'horline',horline);
  508. setappdata(handles.MontageViewingTable,'verline',verline);
  509. setappdata(handles.MontageViewingTable,'horpos',horpos);
  510. setappdata(handles.MontageViewingTable,'verpos',verpos);
  511. end
  512. function click_Montage(src,eventdata,handles)%callback for buttonclick
  513. % % when user click on the montage figure reverse the caluclation from SelectMontage_Callbac
  514. % % to obtain [x y z t] in the original image space
  515. % c = ginput(1);
  516. % horpos = round(c(1,1));
  517. % verpos = round(c(1,2));
  518. % rspan = getappdata(handles.MontageViewingTable,'span');
  519. % ord = getappdata(handles.MontageViewingTable,'ord');
  520. % TableData = get(handles.MontageViewingTable,'Data'); % get User defined parmaeters for the montage
  521. % revord(ord) = [1:4]; % reverse order; this statement defines new variable revord
  522. % for d=1:4, span{d} = rspan{revord(d)}; end
  523. % subcoord(2) = mod(horpos,numel(span{2}));
  524. % subcoord(4) = (horpos - subcoord(2))/numel(span{2})+1;
  525. % subcoord(1) = mod(verpos,numel(span{1}));
  526. % subcoord(3) = (verpos - subcoord(1))/numel(span{1})+1;
  527. % revord(ord) = [1:4]; % reverse order; this statement defines new variable revord
  528. % subcoord
  529. % ord
  530. % revord
  531. % %subcoord = subcoord(revord)
  532. % for d=1:4,
  533. % coord(d) = round( (subcoord(d)-1)*TableData(d,2) + TableData(d,1) );
  534. % coord(d) = min(max(1,coord(d)),handles.dims(d));
  535. % end
  536. % set(handles.Timept,'String',num2str(coord(4)));
  537. % set(handles.TimeptSlider,'value',coord(4));
  538. %
  539. % % redraw crosshairs
  540. % dim1 = numel(span{2}) * numel(span{4}); % rows = ysize*time
  541. % dim2 = numel(span{1}) * numel(span{3}); % columns = xsize*zsize
  542. % horline = getappdata(handles.MontageViewingTable,'horline');
  543. % verline = getappdata(handles.MontageViewingTable,'verline');
  544. % set(horline,'visible','off');set(verline,'visible','off');
  545. % figure(getappdata(handles.MontageViewingTable,'fig'));
  546. % horline = line([1 dim2],[horpos horpos],'color',[0 1 0],'visible','on');
  547. % verline = line([verpos verpos],[1 dim1],'color',[0 1 0],'visible','on');
  548. % setappdata(handles.MontageViewingTable,'horline',horline);
  549. % setappdata(handles.MontageViewingTable,'verline',verline);
  550. % setappdata(handles.Sagitalview,'slicenum',coord(1));
  551. % setappdata(handles.Coronalview,'slicenum',coord(2));
  552. % setappdata(handles.Axialview,'slicenum',coord(3));
  553. % set(handles.Timept,'String',num2str(coord(4)));
  554. % set(handles.TimeptSlider,'value',coord(4));
  555. % UpdateTimept(coord(4),handles);
  556. function PrintBSRButton_Callback(hObject, eventdata, handles)
  557. val = get(hObject,'Value');
  558. if val==1
  559. MainVoxTS = getappdata(handles.PrintBSRButton,'bsr_ts')
  560. set(hObject,'Value',0);
  561. end
  562. %** Also print out timeseries of other datasets.
  563. if ~isempty(handles.OtherImgData)
  564. x = getappdata(handles.Sagitalview,'slicenum');
  565. y = getappdata(handles.Coronalview,'slicenum');
  566. z = getappdata(handles.Axialview,'slicenum');
  567. Tseries = squeeze(handles.BSR.img(x,y,z,:));
  568. num_OtherData = length(handles.OtherImgData);
  569. tmparray = zeros(num_OtherData, length(Tseries));
  570. for l=1:num_OtherData
  571. tmparray(l,:) = squeeze(handles.OtherImgData{l}(x,y,z,:));
  572. end
  573. tmparray = tmparray';
  574. OtherVoxTS = tmparray
  575. end
  576. function MontageViewingTable_CellEditCallback(hObject, eventdata, handles)
  577. val = round(str2num(eventdata.EditData)); % get cell content as entered by user
  578. inds = eventdata.Indices; % find (i,j) position in the table
  579. % if new value is outside of alowable dims, then change back to previous
  580. % cell content
  581. if ~(val >=1 & val <= handles.dims(inds(1)))
  582. TableData = get(handles.MontageViewingTable,'Data');
  583. TableData(inds(1),inds(2)) = eventdata.PreviousData;
  584. set(handles.MontageViewingTable,'Data',TableData);
  585. end
  586. function MontageViewingTable_CellSelectionCallback(hObject, eventdata, handles)
  587. % hObject handle to MontageViewingTable (see GCBO)
  588. % eventdata structure with the following fields (see UITABLE)
  589. % Indices: row and column indices of the cell(s) currently selecteds
  590. % handles structure with handles and user data (see GUIDATA)
  591. % NK: note we must have this function even if it doesn't do anything!!!!!
  592. function [cmap,ignore_pts] = set_colormap(max_bsr, min_bsr, pos_bsr_thresh, neg_bsr_thresh)
  593. % written by Jimmy - NK made some changes
  594. % set the display colormap based on the max/min display values and
  595. % the threshold setting.
  596. %
  597. % The upper colors are coming from the entries of [140:239] of the
  598. % 255 jet colormap, and the lower colors are from the entries of
  599. % [1:100] of the colormap.
  600. %
  601. range_interval = max_bsr - min_bsr;
  602. upper_interval = max_bsr - pos_bsr_thresh;
  603. lower_interval = neg_bsr_thresh - min_bsr; % abs(min_bsr) - abs(neg_bsr_thresh);
  604. % colormap entries for the upper range values, using the
  605. % entries of [140:239] from the 255 jet colormap
  606. %
  607. num_upper_colors = 0;
  608. if (upper_interval > 0)
  609. num_upper_colors = round(upper_interval / range_interval * 255);
  610. cmap_size = round(255 * num_upper_colors/100);
  611. first_color_idx = round(140 / 255 * cmap_size);
  612. last_color_idx = first_color_idx + num_upper_colors - 1;
  613. uppermap = jet(cmap_size);
  614. upper_colors = uppermap(first_color_idx:last_color_idx,:);
  615. end;
  616. % colormap entries for the lower range values, using the
  617. % entries of [1:100] from the 255 jet colormap
  618. %
  619. num_lower_colors = 0;
  620. if (lower_interval > 0)
  621. num_lower_colors = round(lower_interval / range_interval * 255);
  622. cmap_size = round(255 * num_lower_colors/100);
  623. first_color_idx = 1;
  624. last_color_idx = num_lower_colors;
  625. lowermap = jet(cmap_size);
  626. lower_colors = lowermap(first_color_idx:last_color_idx,:);
  627. end;
  628. cmap = zeros(256,3);
  629. cmap(1:255,:) = jet(255);
  630. ignore_pts = [(num_lower_colors+1):(255-num_upper_colors)];
  631. if (num_lower_colors > 0),
  632. cmap(1:num_lower_colors,:) = lower_colors;
  633. end;
  634. if (num_upper_colors > 0),
  635. if ~isempty(ignore_pts)
  636. cmap((ignore_pts(end)+1):255,:) = upper_colors;
  637. else
  638. cmap((255-num_upper_colors+1):255,:) = upper_colors;
  639. end
  640. end
  641. if ~isempty(ignore_pts)
  642. %cmap(ignore_pts,:) = ones(length(ignore_pts),3) * 140/255;
  643. cmap(ignore_pts,:) = gray(numel(ignore_pts));
  644. end
  645. cmap(256,:) = [1 1 1];
  646. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  647. %%%%%%%%% Create functions
  648. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  649. function BSR_posthresh_CreateFcn(hObject, eventdata, handles)
  650. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
  651. set(hObject,'BackgroundColor','white');
  652. end
  653. function BSR_negthresh_CreateFcn(hObject, eventdata, handles)
  654. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
  655. set(hObject,'BackgroundColor','white');
  656. end
  657. function MNI_coord_CreateFcn(hObject, eventdata, handles)
  658. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
  659. set(hObject,'BackgroundColor','white');
  660. end
  661. function Coord_CreateFcn(hObject, eventdata, handles)
  662. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
  663. set(hObject,'BackgroundColor','white');
  664. end
  665. function TimeptSlider_CreateFcn(hObject, eventdata, handles)
  666. if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
  667. set(hObject,'BackgroundColor',[.9 .9 .9]);
  668. end
  669. function Timept_CreateFcn(hObject, eventdata, handles)
  670. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
  671. set(hObject,'BackgroundColor','white');
  672. end
  673. function CoronalSlider_CreateFcn(hObject, eventdata, handles)
  674. if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
  675. set(hObject,'BackgroundColor',[.9 .9 .9]);
  676. end
  677. function SagitalSlider_CreateFcn(hObject, eventdata, handles)
  678. if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
  679. set(hObject,'BackgroundColor',[.9 .9 .9]);
  680. end
  681. function AxialSlider_CreateFcn(hObject, eventdata, handles)
  682. if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
  683. set(hObject,'BackgroundColor',[.9 .9 .9]);
  684. end
  685. function SelectMontage_CreateFcn(hObject, eventdata, handles)
  686. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
  687. set(hObject,'BackgroundColor','white');
  688. end
  689. %** Added clamp range textbox
  690. % --- Executes during object creation, after setting all properties.
  691. function TextboxClampRange_CreateFcn(hObject, eventdata, handles)
  692. if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
  693. set(hObject,'BackgroundColor','white');
  694. end